home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / rexx / imc / rexx-imc.5 / shell.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-25  |  8.0 KB  |  228 lines

  1. /* A Command Shell for REXX/imc                 (C) Ian Collier 1992 */
  2.  
  3. #include "functions.h"
  4. #include <sys/wait.h>
  5. #include<unistd.h>
  6. #include<errno.h>
  7. #include<sys/param.h>
  8. #include<stdlib.h>
  9.  
  10. typedef struct _hashitem {  /* An item in the hash table of path names */
  11.    struct _hashitem *next;  /* The next item in the bucket */
  12.    int hits;                /* Number of times this has been found */
  13.    int expense;             /* Position within $PATH */
  14.    int dot;                 /* Whether dot occurred in the path before this */
  15.    int data;                /* Offset from end of header to data */
  16. } hashitem;
  17.  
  18. static unsigned hashfn();       /* the hash function to make int from string */
  19. static hashitem **hashcreate(); /* Create a hash table */
  20. static void *search();          /* search in the hash table */
  21. static char *locate();          /* find out the path for a command */
  22. static void hashcmd();          /* Execute the "hash" builtin command */
  23. static void hashdel();          /* Delete an element from the hash table */
  24.  
  25. char **arguments=0; /* An array to hold the argument list */
  26. unsigned argnum=0;  /* The number of elements allocated so far */
  27. hashitem **hashtable;
  28. int hashbuckets;
  29.  
  30. int shell(command) /* Execute a UNIX command.  The command must be writable */
  31. char *command;     /* and zero-terminated */
  32. {
  33.    int argc=0;
  34.    char quote=0;
  35.    char c;
  36.    int i,j;
  37.    int pid;
  38.    char *exec;
  39.  
  40.    if(!arguments)                   /* Allocate some initial memory */
  41.       arguments=(char**)allocm((argnum=20)*sizeof(char*)),
  42.       hashtable=hashcreate(hashbuckets=251);
  43.    while(command[0]==' ')command++; /* Ignore leading spaces */
  44.    arguments[argc++]=command;       /* Store the start of arg[0] */
  45.    for(i=j=0;c=command[j];j++){     /* Start tokenising... */
  46.       if(c==quote){quote=0;continue;}
  47.       if(quote){command[i++]=c;continue;}
  48.       if(c=='\''||c=='\"'){quote=c;continue;}
  49.       if(c==' '){
  50.          command[i++]=0;
  51.      while(command[++j]==' ');
  52.      j--;
  53.      if(argc+1>=argnum){
  54.         arguments=(char**)realloc((char*)arguments,
  55.            sizeof(char*)*(argnum+=10));
  56.         if(!arguments)die(Emem);
  57.      }
  58.      arguments[argc++]=command+i;
  59.      continue;
  60.       }
  61.       command[i++]=c;
  62.    }
  63.    command[i++]=0;                 /* 0-terminate the last argument */
  64.    if(!arguments[argc-1][0])argc--;/* In case there were trailing spaces */
  65.    if(!argc)return 0;              /* Null string: just return */
  66.    arguments[argc++]=0;            /* Add the terminating NULL */
  67.    if(!strcmp(arguments[0],"hash"))/* "hash" is built in */
  68.       return hashcmd(arguments),0;
  69.    exec=locate(arguments[0]);      /* Locate the command */
  70.    if(!(pid=vfork())){
  71.       execv(exec,arguments);       /* Execute command */
  72.       if(errno==ENOENT)            /* did not exist */
  73.          fprintf(stderr,"%s: Command not found.\n",arguments[0]);
  74.       else perror(exec);           /* some other error */
  75.       _exit(-3);
  76.    }
  77.    if(pid==-1){
  78.       perror("vfork");
  79.       return -3;
  80.    }
  81.    i=0;
  82.    waitpid(pid,&i,0);             /* Wait for command */
  83.    return (int)(char)(i/256);
  84. }
  85.  
  86. static unsigned hashfn(string,buckets)  /* A hash function */
  87. char *string;         /* the string to hash */
  88. int buckets;          /* the number of hash buckets */
  89. {
  90.    register unsigned i=0;
  91.    while(*string)i+=(i<<3)+*string++;
  92. /* return (((i*40503L)&65535)*buckets)/65536;*/ /* multiplicative hashing: when
  93.                                                  buckets is a power of 2 */
  94.    return i%buckets;        /* division method: when buckets is a prime such
  95.                             that 16^k=a(mod buckets) for small k and a */
  96. }
  97.  
  98. static hashitem **hashcreate(buckets)
  99.                           /* Create hash table as array of null pointers */
  100. int buckets;              /* Number of buckets in hash table */
  101. {
  102.    hashitem **table=(hashitem**)allocm(buckets*sizeof(char *));
  103.    int i;
  104.    for(i=0;i<buckets;table[i++]=0);
  105.    return table;
  106. }
  107.  
  108. static void *search(name,exist) /* Search for a name in the hash table    */
  109. char *name;                     /* if exist=1, the result is a pointer to */
  110. int *exist;                     /* the item; if exist=0 the result is a   */
  111.                                 /* "next" field where the new item would  */
  112. {                               /* be inserted                            */
  113.    int h=hashfn(name,hashbuckets);
  114.    hashitem **i=&hashtable[h];
  115.    hashitem *j;
  116.    int c;
  117.    if(!(j=*i)) return *exist=0,(void *)i; /* No elements in this bucket */
  118.    while(c=strcmp(name,(char *)(j+1))){   /* stop when correct element found */
  119.       if(c<0) return *exist=0,(void *)i;  /* gone too far down the list */
  120.       i=&(j->next);
  121.       if(!(j=*i)) return *exist=0,(void *)i; /* no next element in list */
  122.    }
  123.    return *exist=1,(void *)j;
  124. }
  125.  
  126. static char *locate(name)  /* Locate the executable file "name" */
  127. char *name;
  128. {
  129.    char *path=getenv("PATH");
  130.    void *hash;
  131.    int exist;
  132.    int dot=0;
  133.    int i;
  134.    int dirs=0;
  135.    hashitem *new,**old;
  136.    char *ans;
  137.    static char test[MAXPATHLEN+1];
  138.    if(!strchr(name,'/')){             /* only search if the name has no '/'s */
  139.       hash=search(name,&exist);       /* first search the hash table */
  140.       if(!exist&&path) while(path[0]){/* then search the path */
  141.          dirs++;
  142.          for(i=0;(test[i]=path[0])&&path++[0]!=':';i++); /* Copy next dir */
  143.      if(i==1&&test[0]=='.'){dot=1;continue;} /* Test for "." */
  144.      test[i]='/';
  145.      strcpy(test+i+1,name);                  /* add slash and name */
  146.      if(!access(test,X_OK)){                 /* if it is executable... */
  147.         new=(hashitem *)                     /* make a new hash item */
  148.            allocm(sizeof(hashitem)+strlen(name)+strlen(test)+2);
  149.         old=(hashitem **)hash; /* this points to the previous link field */
  150.             new->next=*old;
  151.         *old=new;
  152.         new->dot=dot;
  153.         new->hits=0;
  154.         new->expense=dirs;
  155.         new->data=strlen(name)+1;
  156.         strcpy((char *)(new+1),name);
  157.         strcpy((char *)(new+1)+new->data,test);
  158.         exist=1;
  159.         hash=(void*)new;
  160.         break;
  161.      }
  162.       }
  163.       if(exist){ /* Now, if the hash item was found or newly created, use it */
  164.          new=(hashitem *)hash;
  165.      new->hits++;
  166.      ans=(char *)(new+1)+new->data;
  167.      if(new->dot&&!access(name,X_OK)) /* If "." came in the path before */
  168.         return name;                  /* the named directory, check "." */
  169.      return ans;                      /* first, then return the stored  */
  170.       }                                   /* name.                          */
  171.    }
  172.    return name; /* if the name contains '/' or wasn't found in the path,
  173.                    return it unchanged. */
  174. }
  175.  
  176. static void hashdel(name)  /* delete name from hash table, if present */
  177. char *name;
  178. {
  179.    int h=hashfn(name,hashbuckets);
  180.    hashitem **i=&hashtable[h];
  181.    hashitem *j;
  182.    int c;
  183.    if(!(j=*i)) return;                   /* No items in this bucket */
  184.    while(c=strcmp(name,(char *)(j+1))){  /* search for the given name */
  185.       if(c<0) return;
  186.       i=&(j->next);
  187.       if(!(j=*i)) return;
  188.    }
  189.    *i=j->next;                        /* link the next item to the previous  */
  190.    free(j);                           /* so deleting this one from the chain */
  191. }
  192.  
  193. void hashclear()          /* Clear the hash table (eg when PATH changes) */
  194. {
  195.    int j;
  196.    hashitem *h,*k;
  197.    for(j=0;j<hashbuckets;j++)
  198.       for(h=hashtable[j],hashtable[j]=0;h;h=k){
  199.          k=h->next;        /* find the address of the next item before */
  200.          free((char *)h);  /* freeing this one (obviously).            */
  201.       }
  202. }
  203.  
  204. static void hashcmd(args) /* Implement the "hash" builtin command */
  205. char *args[];
  206. {
  207.    int i;
  208.    int j;
  209.    int hits;
  210.    hashitem *h;
  211.    if(args[i=1])          /* some arguments exist */
  212.       while(args[i]){
  213.          if(!strcmp(args[i],"-r")) hashclear();  /* Clear table */
  214.      else locate(args[i]);      /* add argument to table */
  215.      i++;
  216.       }
  217.    else{                  /* no arguments: print table */
  218.       hits=0;
  219.       for(i=0;i<hashbuckets;i++)
  220.          for(j=0,h=hashtable[i];h;h=h->next){
  221.         if(!hits++) puts(" hits    cost    command");
  222.         putchar(j++?'+':' ');
  223.             printf("%-7d %-7d %s\n",h->hits,h->expense,(char*)(h+1)+h->data);
  224.      }
  225.       if(!hits) puts("No commands in hash table.");
  226.    }
  227. }
  228.